﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;

namespace Lichtspiel.Audio
{
    public class SoundBuffer : IDisposable
    {
        // TODO looping, speed, volume, mp3, ogg
        private bool disposed = false;
        private int buffer;

        internal int ID {
            get { return buffer; }
        }

        public SoundBuffer(string file) {
            buffer = AL.GenBuffer();

            int channels, bitsPerSample, sampleRate;
            byte[] soundData = LoadWave(File.Open(file, FileMode.Open), out channels, out bitsPerSample, out sampleRate);
            AL.BufferData(buffer, GetSoundFormat(channels, bitsPerSample), soundData, soundData.Length, sampleRate);
        }

        // Loads a wave/riff audio file.
        private static byte[] LoadWave(Stream stream, out int channels, out int bits, out int rate) {
            if (stream == null)
                throw new ArgumentNullException("stream");

            using (BinaryReader reader = new BinaryReader(stream)) {
                // RIFF header
                string signature = new string(reader.ReadChars(4));
                if (signature != "RIFF")
                    throw new NotSupportedException("Specified stream is not a wave file.");

                int riffChunckSize = reader.ReadInt32();

                string format = new string(reader.ReadChars(4));
                if (format != "WAVE")
                    throw new NotSupportedException("Specified stream is not a wave file.");

                // WAVE header
                string format_signature = new string(reader.ReadChars(4));
                if (format_signature != "fmt ")
                    throw new NotSupportedException("Specified wave file is not supported.");

                int formatChunkSize = reader.ReadInt32();
                int audioFormat = reader.ReadInt16();
                int numChannels = reader.ReadInt16();
                int sampleRate = reader.ReadInt32();
                int byteRate = reader.ReadInt32();
                int blockAlign = reader.ReadInt16();
                int bitsPerSample = reader.ReadInt16();

                string dataSignature = new string(reader.ReadChars(4));
                if (dataSignature != "data")
                    throw new NotSupportedException("Specified wave file is not supported.");

                int dataChunkSize = reader.ReadInt32();

                channels = numChannels;
                bits = bitsPerSample;
                rate = sampleRate;

                return reader.ReadBytes((int)reader.BaseStream.Length);
            }
        }

        private static ALFormat GetSoundFormat(int channels, int bits) {
            switch (channels) {
                case 1: return bits == 8 ? ALFormat.Mono8 : ALFormat.Mono16;
                case 2: return bits == 8 ? ALFormat.Stereo8 : ALFormat.Stereo16;
                default: throw new NotSupportedException("The specified sound format is not supported.");
            }
        }

        #region IDisposable Members

        public void Dispose() {
            Dispose(true);
        }

        private void Dispose(bool disposing) {
            // If you need thread safety, use a lock around these 
            // operations, as well as in your methods that use the resource.
            if (!disposed) {
                if (disposing) {
                    
                }
                AL.DeleteBuffer(buffer);
                // Indicate that the instance has been disposed.
                disposed = true;
            }
        }

        #endregion
    }
}
